#ifndef BEANMAPPER_H
#define BEANMAPPER_H
#include <QSharedPointer>
#include <QMetaObject>

#ifdef DEST_LIB
    #if defined(SQL_MAPPPER_EXPORT)
    #  define SQL_MAPPER_EXPORT Q_DECL_EXPORT
    #else
    #  define SQL_MAPPER_EXPORT Q_DECL_IMPORT
    #endif
#else
     #  define SQL_MAPPER_EXPORT


#endif


namespace DB {
class SqlOperator;
struct DataBaseConfig
{
    QString host            = "127.0.0.1";
    QString userName        = "root";
    QString passWord        = "root";
    quint16 port            = 3360;
    QString sqlType         = "QSQLITE";
    QString sqlConnection   = "qt_sql_default_connection";
    QString sqlDataBaseName = "default.db";
};
template<typename T>
void argsToStrValHash(const T &t){
    Q_UNUSED(t)
}



template <typename ...Args >
static void argsToStrValHash(QHash<QString,QVariant> & data,const QString& key,const QVariant &value,Args&& ... args)
{
    data.insert(key,value);
    argsToStrValHash(data,args...);
}


static DataBaseConfig defaultConfig;
SQL_MAPPER_EXPORT class BeanMapper
{
public:
    BeanMapper();
    ~BeanMapper();

    bool openDB(const DataBaseConfig &config = defaultConfig);

    template<typename T>
    /**
     * @brief hasBeanTable 查询数据中 是否存在这个 Bean 对应的数据表
     * @return
     */
    bool hasBeanTable();

    template<typename T>
    void makeTable();

    template<typename T>
    QList<T> select(const QHash<QString, QVariant> &where = QHash<QString, QVariant>());

    template<typename T ,typename ...Args>
    QList<T> selectWhereArgs(Args&& ...args);

    template<typename T>
    T selectById(int ID);

    template<typename T>
    QList<QVariant> selectColnum(const QString& colName);

    template<typename T>
    bool insert(const T& t);

    template<typename T>
    bool insert(const QList<T>& ts);
    template<typename T>
    /**
     * @brief update  根据where 内容匹配  sql  where 条件 仅支持  key = value 的形式
     * @param updata  更新的数据内容
     * @param where   条件集合
     * @return
     */
    bool update(const T & updata, const QHash<QString, QVariant> &where);


    template<typename T ,typename ...Args>
    ///
    /// \brief updateWhereArgs   根据Args 内容匹配  sql  where 条件 仅支持  key = value 的形式
    /// \param updata  更新的数据内容
    /// \param args    更新的条件  {args[0]:key  args[1]:value } {args[2]:key  args[3]:value }...
    /// \return
    ///
    bool updateWhereArgs(const T & updata,Args &&... args);


    template<typename T>
    bool deleteRow(QHash<QString,QVariant> &where );


    template<typename T ,typename ...Args>
    bool deleteRowWhereArgs( Args && ...args );



    const QString &lastError();

    const QString lastQuerySql();

private:
    bool __hasBeanTable(const QMetaObject & metaObj);
    template <typename T>
    bool __select_p(QList< T>& t,const QHash<QString, QVariant> &where);
    void __makeTable(const QMetaObject & metaObj );
    bool __select_opt(const QString &tableName, const QList<QString> &fields, QList<QVariantList> &data ,const QHash<QString, QVariant> &where = QHash<QString, QVariant>());
    bool __metaToSelectMapper(const QMetaObject& metaObj,QList<QString> &selectMapper,bool autoUp =false);
    bool __mapperToObject(QObject* obj,const QList<QString> & fild,const QVariantList &values);
    bool __insert(const QMetaObject &metaObj, const QObject *obj);
    bool __insert(const QMetaObject &metaObj, const QList<const QObject *> &objs);
    bool __selectColnum(const QMetaObject & metaObject, const QString& colName, QList<QVariant> &selectData);
    bool __queryIdPropertyName(const QMetaObject & metaObj, QString &where);
    bool __update(const QMetaObject &metaObj , const QObject *obj, const QHash<QString,QVariant>&where);
    bool __deleteRow(const QMetaObject &metaObj, const QHash<QString,QVariant> &where);
private :
    DB::SqlOperator* mp_SqlOperator;
};

template<typename T, typename... Args>
QList<T> BeanMapper::selectWhereArgs(Args&&...args)
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    static_assert(sizeof...(args)%2 ==0 ,"args count %2 != 0");
    QHash<QString,QVariant> mapList;
    argsToStrValHash(mapList,args...);
    return select<T>(mapList);
}

template<typename T>
bool BeanMapper::hasBeanTable()
{
    return __hasBeanTable(T::staticMetaObject);
}


template<typename T, typename ...Args>
bool BeanMapper::deleteRowWhereArgs(Args&& ...args)
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    static_assert(sizeof...(args)%2 ==0 ,"args count %2 != 0");
    QHash<QString,QVariant> mapList;
    argsToStrValHash(mapList,args...);
    return __deleteRow(T::staticMetaObject,mapList);
}


template<typename T>
bool BeanMapper::deleteRow(QHash<QString, QVariant> &where)
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    return __deleteRow(T::staticMetaObject,where);
}

template<typename T, typename ...Args>
bool BeanMapper::updateWhereArgs(const T &updata, Args&&...args)
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    static_assert(sizeof...(args)%2 ==0 ,"args count %2 != 0");
    QHash<QString,QVariant> mapList;
    argsToStrValHash(mapList,args...);
    return  __update(T::staticMetaObject,&updata,mapList);

}

template<typename T>
bool BeanMapper::update(const T &updata, const QHash<QString, QVariant> &where)
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    return __update(T::staticMetaObject,&updata,where);
}

template<typename T>
void BeanMapper::makeTable()
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    __makeTable(T::staticMetaObject);
}

template<typename T>
bool BeanMapper::insert(const QList<T> &ts)
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    QList<const QObject*> objs;

    QList<T>::const_iterator  it=ts.begin();
    while (it!= ts.end()) {

        objs.append(&*it);
        it++;
    }

    return __insert(T::staticMetaObject,objs);
}

template<typename T>
QList<QVariant> BeanMapper::selectColnum(const QString &colName)
{
    QList<QVariant> selectDat;
    __selectColnum(T::staticMetaObject,colName,selectDat);
    return selectDat;
}

template<typename T>
T BeanMapper::selectById(int ID)
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    int n = 0;
    QList<QString> selectMapper;
    if(__metaToSelectMapper(T::staticMetaObject,selectMapper))
    {
        QString whereKey;
        QList<QVariantList> data;
        __queryIdPropertyName(T::staticMetaObject,whereKey);
        QHash<QString,QVariant>where;
        where.insert(whereKey,ID);
        if( __select_opt(T::staticMetaObject.className(),selectMapper,data,where))
        {
            for(int i = 0 ; i < data.size() ;i++)
            {
                T t ;
                __mapperToObject(&t,selectMapper,data[i]) ?n++:n;
                return t;
            }
        }
    }
    return T();
}


template<typename T>
bool BeanMapper::insert(const T &t)
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    return __insert(T::staticMetaObject,&t);
}


template<typename T>
QList<T > BeanMapper::select(const QHash<QString, QVariant> &where )
{
    static_assert( std::is_base_of<QObject,T>::value,"The base class of this select class is not QObjecet");
    QList<T> t;
    __select_p<T>(t,where);
    return t;
}


template<typename T>
bool BeanMapper::__select_p(QList<T> &values, const QHash<QString, QVariant> &where)
{
    int n = 0;
    QList<QString> selectMapper;
    if(__metaToSelectMapper(T::staticMetaObject,selectMapper))
    {
        QList<QVariantList> data;
        if( __select_opt(T::staticMetaObject.className(),selectMapper,data,where))
        {


            for(int i = 0 ; i < data.size() ;i++)
            {
                T t ;
                __mapperToObject(&t,selectMapper,data[i]) ?n++:n;
                values.append(t);
            }
        }
    }

    return (n==0);
}
}
#endif // BEANMAPPER_H
